<template>
  <div class="process-basic">
    <h2>流程管理</h2>

  <div class="toolbar">
      <input class="pretty-input" v-model.trim="search.keyword" placeholder="按流程名称/编号搜索" @keyup.enter="onSearch" />
      <div class="select-like" @click="toggleTypeDropdown" ref="typeDropdownRef">
        <span class="label">{{ selectedTypeName }}</span>
        <span class="arrow">▼</span>
        <ul v-if="typeDropdownOpen" class="select-menu">
          <li :class="{active: !search.typeId}" @click.stop="selectType(null)">全部类型</li>
          <li v-for="t in types" :key="t.typeId" :class="{active: search.typeId === t.typeId}" @click.stop="selectType(t.typeId)">{{ t.typeName }}</li>
        </ul>
      </div>
      <button @click="onSearch">查询</button>
      <button class="plain" @click="onReset">重置</button>
      <button class="primary" @click="onAddProcessClick">新增流程</button>
    </div>

    <table class="table" v-if="true">
      <thead>
        <tr>
          <th>ID</th>
          <th>流程编号</th>
          <th>流程名称</th>
          <th>审批类型</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="p in processes" :key="p.id">
          <td>{{ p.id }}</td>
          <td>{{ p.processCode }}</td>
          <td>{{ p.processName }}</td>
          <td><span class="tag">{{ p.typeName || p.typeCode }}</span></td>
          <td>
            <button @click="onShowDetails(p)">详情</button>
            <button @click="onEditPreset(p)">编辑预设</button>
            <button class="danger" @click="onDeleteProcessClick(p)">删除</button>
          </td>
        </tr>
        <tr v-if="processes.length === 0">
          <td colspan="5" style="text-align:center;color:#909399;">暂无数据</td>
        </tr>
      </tbody>
    </table>
    <div v-else class="debug-empty">表格未渲染（调试占位）</div>

    <div class="pagination">
      <div class="page-size">
        <span>每页</span>
        <div class="page-size-group">
          <button type="button" class="page-size-pill" :class="{ active: pageSize === 10 }" @click="setPageSize(10)">10</button>
          <button type="button" class="page-size-pill" :class="{ active: pageSize === 20 }" @click="setPageSize(20)">20</button>
          <button type="button" class="page-size-pill" :class="{ active: pageSize === 50 }" @click="setPageSize(50)">50</button>
        </div>
      </div>
      <div class="pager-controls">
        <button :disabled="currentPage===1" @click="goPrev">上一页</button>
        <span>第 {{ currentPage }} / {{ totalPage }} 页</span>
        <button :disabled="currentPage>=totalPage" @click="goNext">下一页</button>
      </div>
    </div>

    <div v-if="processDialog.visible" class="dialog-mask">
      <div class="dialog">
        <h3>新增流程</h3>
        <div class="form-row">
          <label>流程名称</label>
          <input class="pretty-input" v-model="processForm.processName" placeholder="请输入流程名称" />
        </div>
        <div class="form-row">
          <label>流程编号</label>
          <input class="pretty-input" :value="'自动生成'" disabled />
        </div>
        <div class="form-row">
          <label>审批类型</label>
          <div class="select-like" @click="toggleCreateTypeDropdown" ref="createTypeDropdownRef">
            <span class="label">{{ selectedCreateTypeName }}</span>
            <span class="arrow">▼</span>
            <ul v-if="createTypeDropdownOpen" class="select-menu">
              <li :class="{active: !processForm.typeId}" @click.stop="selectCreateType(null)">请选择审批类型</li>
              <li v-for="type in types" :key="type.typeId"
                  :class="{active: processForm.typeId === type.typeId, disabled: usedTypeCodes.has(type.typeCode)}"
                  @click.stop="selectCreateType(type.typeId)">
                {{ type.typeId }} - {{ type.typeName }}{{ usedTypeCodes.has(type.typeCode) ? '（已使用）' : '' }}
              </li>
            </ul>
          </div>
        </div>
        <div class="dialog-actions">
          <button @click="onSubmitProcess" :disabled="!processForm.typeId || isSelectedTypeUsed">确定</button>
          <button class="plain" @click="onCancelProcess">取消</button>
        </div>
      </div>
    </div>
  </div>

  <!-- 详情弹窗：显示流程详情预设表 -->
  <div v-if="detailDialog.visible" class="dialog-mask">
    <div class="dialog" style="width: 800px;">
      <h3>流程预设详情 - {{ detailDialog.title }}</h3>
      <table class="table">
        <thead>
          <tr>
            <th>预设编号</th>
            <th>节点编号</th>
            <th>上一节点</th>
            <th>下一节点</th>
            <th>审批人</th>
            <th>类型</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="d in details" :key="d.DefaultProcessDetailCode">
            <td>{{ d.DefaultProcessDetailCode }}</td>
            <td>{{ d.NodeCode }}</td>
            <td>{{ d.LastNodeCode }}</td>
            <td>{{ d.NextNodeCode }}</td>
            <td>{{ d.ApproverName || d.ApproverCode || '-' }}</td>
            <td>{{ d.TypeCode }}</td>
          </tr>
        </tbody>
      </table>
      <div class="dialog-actions">
        <button class="plain" @click="detailDialog.visible = false">关闭</button>
      </div>
    </div>
  </div>

  <!-- 编辑弹窗：顺序编辑单人审批的预设链路 -->
  <div v-if="editDialog.visible" class="dialog-mask">
    <div class="dialog" style="width: 880px;">
      <h3>编辑流程预设 - {{ editDialog.title }}</h3>
      <p style="color:#666;margin:4px 0 12px;">规则：第一行可选“节点编号/下一节点”；其余行仅可填“下一节点”。每行保存后自动推导下一行的“当前节点/上一节点”。最后一行的“下一节点”必须为空才能保存。</p>
      <table class="table">
        <thead>
          <tr>
            <th style="width:180px;">当前节点</th>
            <th style="width:180px;">上一节点</th>
            <th style="width:180px;">下一节点</th>
            <th>审批人（可选）</th>
            <th style="width:80px;">操作</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="(row, idx) in editRows" :key="idx">
            <td>
              <select v-if="idx===0" v-model="row.NodeCode" @change="onNodeChange(idx)">
                <option v-for="n in nodeOptions" :key="n.NodeCode" :value="n.NodeCode" :disabled="isNodeOptionDisabled(n.NodeCode, idx)">
                  {{ n.NodeCode }} - {{ n.NodeName }}{{ isNodeOptionDisabled(n.NodeCode, idx) ? '（审批人重复）' : '' }}
                </option>
              </select>
              <span v-else>{{ row.NodeCode }}</span>
            </td>
            <td>{{ row.LastNodeCode || '-' }}</td>
            <td>
              <select v-model="row.NextNodeCode" @change="onNextChange(idx)">
                <option value="">(空)</option>
                <option v-for="n in nodeOptions" :key="n.NodeCode" :value="n.NodeCode" :disabled="isNodeOptionDisabledForNext(n.NodeCode, idx)">
                  {{ n.NodeCode }} - {{ n.NodeName }}{{ isNodeOptionDisabledForNext(n.NodeCode, idx) ? '（审批人重复）' : '' }}
                </option>
              </select>
            </td>
            <td>
              <div class="approver-display">
                <span v-if="getApproverByNodeCode(row.NodeCode) === '-'">-</span>
                <div v-else class="approver-list">
                  <span 
                    v-for="(approver, idx) in getApproverListByNodeCode(row.NodeCode)" 
                    :key="idx"
                    class="approver-tag"
                  >
                    {{ approver }}
                  </span>
                </div>
              </div>
            </td>
            <td>
              <button 
                v-if="editRows.length > 1" 
                class="delete-row-btn" 
                @click="deleteRow(idx)"
                title="删除此行"
              >
                删除
              </button>
              <span v-else class="no-delete">-</span>
            </td>
          </tr>
        </tbody>
      </table>
      <div class="dialog-actions">
        <button @click="addRow">新增一行</button>
        <button @click="savePreset" :disabled="!canSave">保存</button>
        <button class="plain" @click="editDialog.visible=false">取消</button>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { reactive, ref, onMounted, computed } from 'vue'
import { examineApi } from '@/api'

const processes = ref<{ id: number; processCode: string; processName: string; typeCode: string; typeId?: number; typeName?: string }[]>([])
const types = ref<{ typeId: number; typeCode: string; typeName: string }[]>([])
const currentPage = ref(1)
const totalPage = ref(1)
const pageSize = ref(10)
const search = reactive({ keyword: '', typeId: '' as any })

// 轻量自绘下拉状态
const typeDropdownOpen = ref(false)
const typeDropdownRef = ref<HTMLElement | null>(null)
const selectedTypeName = computed(() => {
  if (!search.typeId) return '全部类型'
  const t = types.value.find(x => x.typeId === search.typeId)
  return t?.typeName || '全部类型'
})

function toggleTypeDropdown() {
  typeDropdownOpen.value = !typeDropdownOpen.value
}
function selectType(id: number | null) {
  search.typeId = (id ?? '') as any
  typeDropdownOpen.value = false
  onSearch()
}
function onClickOutside(e: Event) {
  const el = typeDropdownRef.value
  if (el && !el.contains(e.target as Node)) typeDropdownOpen.value = false
}

const processDialog = reactive({ visible: false })
const processForm = reactive({ processName: '', typeId: 0 })

function onAddProcessClick() {
  processForm.processName = ''
  // 默认选中第一个可用的类型（按 TypeCode 判重），若没有则为 0
  const first = types.value.find(t => !(usedTypeCodes.value as Set<string>).has(t.typeCode))
  processForm.typeId = first ? first.typeId : 0
  processDialog.visible = true
}

// 新增流程弹窗的自绘下拉
const createTypeDropdownOpen = ref(false)
const createTypeDropdownRef = ref<HTMLElement | null>(null)
const selectedCreateTypeName = computed(() => {
  const t = types.value.find(x => x.typeId === processForm.typeId)
  return t?.typeName || '请选择审批类型'
})
function toggleCreateTypeDropdown() {
  createTypeDropdownOpen.value = !createTypeDropdownOpen.value
}
function selectCreateType(id: number | null) {
  if (id) {
    const t = types.value.find(x => x.typeId === id)
    if (t && (usedTypeCodes.value as Set<string>).has(t.typeCode)) return
  }
  processForm.typeId = id ?? 0
  createTypeDropdownOpen.value = false
}

// 已使用的类型编号集合（纯前端基于当前已加载流程列表计算）
const usedTypeIds = computed(() => {
  const set = new Set<number>()
  for (const p of processes.value) {
    if (typeof p.typeId === 'number' && p.typeId > 0) set.add(p.typeId)
  }
  return set
})

// 基于 TypeCode 的占用集合
const usedTypeCodes = computed(() => {
  const set = new Set<string>()
  for (const p of processes.value) {
    if (p.typeCode) set.add(p.typeCode)
  }
  return set
})

const isSelectedTypeUsed = computed(() => {
  const selected = types.value.find(t => t.typeId === processForm.typeId)
  if (!selected?.typeCode) return false
  return (usedTypeCodes.value as Set<string>).has(selected.typeCode)
})

async function loadProcesses() {
  try {
    const res = await examineApi.getProcesses({ 
      pageindex: currentPage.value, 
      pageSize: pageSize.value,
      keyword: search.keyword || undefined,
      typeId: search.typeId || undefined
    })
    // 统一解包 ApiResult<ApiPagin<>>
    const payload: any = (res && (res as any).data) ? (res as any).data : res
    // 常见后端返回形态：
    // 1) { code, data: { TotalCount, TotalPage, data: [...] } }
    // 2) { code, data: { TotalCount, TotalPage, 0:{},1:{},... } }  // 数字键对象
    // 3) { code, data: [...] }
    const pageObj: any = payload?.data ?? payload?.Data ?? {}
    let list: any[] = []
    if (Array.isArray(pageObj)) {
      list = pageObj
    } else if (Array.isArray(pageObj?.data)) {
      list = pageObj.data
    } else {
      const numericKeys = Object.keys(pageObj).filter(k => /^\d+$/.test(k))
      if (numericKeys.length > 0) {
        numericKeys.sort((a,b) => Number(a) - Number(b))
        list = numericKeys.map(k => (pageObj as any)[k])
      }
    }

    processes.value = list.map((x, i) => ({
      id: x.processId ?? x.ProcessId ?? i + 1,
      processCode: x.processCode ?? x.ProcessCode ?? '',
      processName: x.processName ?? x.ProcessName ?? '',
      typeCode: x.typeCode ?? x.TypeCode ?? '',
      typeId: x.typeId ?? x.TypeId ?? undefined,
      typeName: x.typeName ?? x.TypeName ?? '',
    }))

    const totalCount = pageObj?.TotalCount ?? pageObj?.totalCount ?? list.length
    totalPage.value = pageObj?.TotalPage ?? pageObj?.totalPage ?? Math.max(1, Math.ceil((Number(totalCount)||0)/pageSize.value))
  } catch (e) {
    console.error('加载流程失败', e)
  }
}

async function loadTypes() {
  try {
    const res = await examineApi.getTypes({ pageindex: 1, pageSize: 100 })
    const list = (res.data?.data || []) as any[]
    types.value = list.map((x: any) => ({
      typeId: x.typeId ?? x.TypeId ?? 0,
      typeCode: x.typeCode ?? x.TypeCode ?? '',
      typeName: x.typeName ?? x.TypeName ?? '',
    }))
  } catch (e) {
    console.error('加载审批类型失败', e)
  }
}

async function onSubmitProcess() {
  try {
    // 前端二次校验：同类型只允许新增一次
    if (isSelectedTypeUsed.value) {
      alert('该审批类型已存在流程，不能重复新增。')
      return
    }
    const payload: any = {
      ProcessName: processForm.processName || '默认流程',
      TypeId: processForm.typeId || 0
    }
    // 兼容后端当前仍要求 TypeCode 的校验
    const selected = types.value.find(t => t.typeId === processForm.typeId)
    if (selected?.typeCode) {
      payload.TypeCode = selected.typeCode
    }
    await examineApi.createProcess(payload)
    await loadProcesses()
    processDialog.visible = false
  } catch (e) {
    console.error('新增流程失败', e)
  }
}

function onCancelProcess() {
  processDialog.visible = false
}

async function onDeleteProcessClick(p: { id: number }) {
  try {
    await examineApi.deleteProcess({ ProcessId: p.id })
    await loadProcesses()
  } catch (e) {
    console.error('删除流程失败', e)
  }
}

onMounted(() => {
  loadProcesses()
  loadTypes()
  document.addEventListener('click', onClickOutside)
  document.addEventListener('click', (e: Event) => {
    const el = createTypeDropdownRef.value
    if (el && !el.contains(e.target as Node)) createTypeDropdownOpen.value = false
  })
})

function onSearch() {
  currentPage.value = 1
  loadProcesses()
}

function onReset() {
  search.keyword = ''
  search.typeId = ''
  currentPage.value = 1
  loadProcesses()
}

function onPageSizeChange() {
  currentPage.value = 1
  loadProcesses()
}

function setPageSize(size: number) {
  if (pageSize.value === size) return
  pageSize.value = size
  onPageSizeChange()
}

function goPrev() {
  if (currentPage.value > 1) { currentPage.value -= 1; loadProcesses() }
}

function goNext() {
  if (currentPage.value < totalPage.value) { currentPage.value += 1; loadProcesses() }
}

// 预设编辑逻辑
const editDialog = reactive({ visible: false, title: '', processCode: '', typeCode: '' })
const editRows = ref<any[]>([])
const nodeOptions = ref<{ NodeCode: string; NodeName: string; ApproverCode: string; ApproverName?: string }[]>([])

async function onEditPreset(p: { processCode: string; processName: string; typeCode: string }) {
  editDialog.title = p.processName
  editDialog.processCode = p.processCode
  editDialog.typeCode = p.typeCode
  // 节点选项
  const nodesRes = await examineApi.getNodes({ pageindex: 1, pageSize: 9999 })
  nodeOptions.value = ((nodesRes.data?.data || []) as any[]).map(x => ({ 
    NodeCode: x.NodeCode ?? x.nodeCode, 
    NodeName: x.NodeName ?? x.nodeName,
    ApproverCode: x.ApproverCode ?? x.approverCode,
    ApproverName: x.ApproverName ?? x.approverName ?? x.ApproverNicknames ?? x.approverNicknames
  }))
  // 读取当前预设，折叠为单人顺序行
  const res = await examineApi.getDefaultProcessDetails({ processCode: p.processCode, pageindex: 1, pageSize: 999 })
  const list = (res.data?.data || []) as any[]
  // 简单按 DefaultProcessDetailId 或 NodeCode 顺序
  list.sort((a,b)=> (a.DefaultProcessDetailId||0) - (b.DefaultProcessDetailId||0))
  const rows: any[] = []
  for (const d of list) {
    // 若此节点已存在行，则跳过（不做多人）
    if (rows.find(r => r.NodeCode === (d.NodeCode))) continue
    rows.push({
      NodeCode: d.NodeCode || '',
      LastNodeCode: d.LastNodeCode || '',
      NextNodeCode: d.NextNodeCode || '',
      ApproverCode: d.ApproverCode || ''
    })
  }
  if (rows.length === 0) rows.push({ NodeCode: '', LastNodeCode: '', NextNodeCode: '', ApproverCode: '' })
  editRows.value = rows
  editDialog.visible = true
}

function getApproverByNodeCode(nodeCode: string): string {
  if (!nodeCode) return '-'
  const node = nodeOptions.value.find(n => n.NodeCode === nodeCode)
  return node?.ApproverName || node?.ApproverCode || '-'
}

function getApproverListByNodeCode(nodeCode: string): string[] {
  if (!nodeCode) return []
  const node = nodeOptions.value.find(n => n.NodeCode === nodeCode)
  if (!node?.ApproverCode) return []
  
  // 如果有审批人姓名，优先使用姓名
  if (node.ApproverName) {
    return node.ApproverName.split(',').map(name => name.trim()).filter(name => name)
  }
  
  // 否则使用审批人编号
  return node.ApproverCode.split(',').map(code => code.trim()).filter(code => code)
}

// 获取节点的审批人ID串（仅用于提交）
function getApproverCodeStringByNodeCode(nodeCode: string): string {
  if (!nodeCode) return ''
  const node = nodeOptions.value.find(n => n.NodeCode === nodeCode)
  return node?.ApproverCode || ''
}

// 解析审批人ID字符串为数组
function parseApproverIds(codeStr: string): string[] {
  if (!codeStr) return []
  return codeStr.split(',').map(id => id.trim()).filter(id => id)
}

// 获取当前已使用的审批人集合（排除指定行）
function getUsedApproverSet(excludeRowIndex?: number): Set<string> {
  const usedApprovers = new Set<string>()
  
  editRows.value.forEach((row, index) => {
    if (excludeRowIndex !== undefined && index === excludeRowIndex) return
    
    if (row.NodeCode) {
      // 使用审批人编号进行比较，而不是姓名
      const node = nodeOptions.value.find(n => n.NodeCode === row.NodeCode)
      if (node?.ApproverCode) {
        const approvers = parseApproverIds(node.ApproverCode)
        approvers.forEach(approver => usedApprovers.add(approver))
      }
    }
  })
  
  return usedApprovers
}

// 检查当前节点选项是否应该被禁用
function isNodeOptionDisabled(nodeCode: string, rowIndex: number): boolean {
  if (!nodeCode) return false
  
  const usedApprovers = getUsedApproverSet(rowIndex)
  const node = nodeOptions.value.find(n => n.NodeCode === nodeCode)
  if (!node?.ApproverCode) return false
  
  const nodeApprovers = parseApproverIds(node.ApproverCode)
  
  // 如果该节点的任何审批人已被使用，则禁用
  return nodeApprovers.some(approver => usedApprovers.has(approver))
}

// 检查下一节点选项是否应该被禁用
function isNodeOptionDisabledForNext(nextNodeCode: string, currentRowIndex: number): boolean {
  if (!nextNodeCode) return false
  
  const usedApprovers = getUsedApproverSet() // 获取所有已使用的审批人
  const node = nodeOptions.value.find(n => n.NodeCode === nextNodeCode)
  if (!node?.ApproverCode) return false
  
  const nextNodeApprovers = parseApproverIds(node.ApproverCode)
  
  // 如果该节点的任何审批人已被使用，则禁用
  return nextNodeApprovers.some(approver => usedApprovers.has(approver))
}

function onNodeChange(idx: number) {
  // 当第一行节点变更时，更新第一行的 LastNodeCode，并连带下一行的 NodeCode/LastNodeCode
  const rows = editRows.value
  if (idx === 0) {
    rows[0].LastNodeCode = ''
    if (rows.length > 1) {
      rows[1].NodeCode = rows[0].NextNodeCode || ''
      rows[1].LastNodeCode = rows[0].NodeCode || ''
    }
  }
}

function onNextChange(idx: number) {
  const rows = editRows.value
  const current = rows[idx]
  const next = rows[idx + 1]
  if (next) {
    next.NodeCode = current.NextNodeCode || ''
    next.LastNodeCode = current.NodeCode || ''
  }
}

function addRow() {
  const rows = editRows.value
  const last = rows[rows.length - 1]
  rows.push({
    NodeCode: last?.NextNodeCode || '',
    LastNodeCode: last?.NodeCode || '',
    NextNodeCode: '',
    ApproverCode: ''
  })
}

function deleteRow(idx: number) {
  const rows = editRows.value
  if (rows.length <= 1) return // 至少保留一行
  
  // 删除指定行
  rows.splice(idx, 1)
  
  // 重新计算后续行的节点关系
  for (let i = idx; i < rows.length; i++) {
    if (i === 0) {
      // 第一行：清空上一节点
      rows[i].LastNodeCode = ''
    } else {
      // 其他行：从上一行获取节点信息
      const prevRow = rows[i - 1]
      rows[i].NodeCode = prevRow.NextNodeCode || ''
      rows[i].LastNodeCode = prevRow.NodeCode || ''
    }
  }
}

const canSave = computed(() => {
  const rows = editRows.value
  if (!rows.length) return false
  // 最后一行必须没有下一节点
  return !(rows[rows.length - 1].NextNodeCode)
})

async function savePreset() {
  if (!canSave.value) return
  
  // 验证是否有重复的审批人
  const allUsedApprovers = new Set<string>()
  const duplicateApprovers = new Set<string>()
  
  for (const row of editRows.value) {
    if (row.NodeCode) {
      const node = nodeOptions.value.find(n => n.NodeCode === row.NodeCode)
      if (node?.ApproverCode) {
        const approvers = parseApproverIds(node.ApproverCode)
        for (const approver of approvers) {
          if (allUsedApprovers.has(approver)) {
            duplicateApprovers.add(approver)
          } else {
            allUsedApprovers.add(approver)
          }
        }
      }
    }
  }
  
  if (duplicateApprovers.size > 0) {
    alert(`发现重复的审批人：${Array.from(duplicateApprovers).join(', ')}。请确保不同节点之间的审批人不重复。`)
    return
  }
  
  const rows = editRows.value
  // 组装后端需要的扁平明细
  const detailsPayload = rows.map(r => ({
    DefaultProcessDetailCode: '',
    NodeCode: r.NodeCode,
    LastNodeCode: r.LastNodeCode || '',
    NextNodeCode: r.NextNodeCode || '',
    // 始终按审批人ID提交
    ApproverCodes: (() => {
      const codeStr = getApproverCodeStringByNodeCode(r.NodeCode)
      return codeStr ? codeStr.split(',').map(x => x.trim()).filter(Boolean) : []
    })(),
    TypeCode: editDialog.typeCode
  }))
  await examineApi.saveDefaultProcessDetails({ ProcessCode: editDialog.processCode, Details: detailsPayload })
  editDialog.visible = false
  // 刷新详情视图
  await onShowDetails({ id: 0, processCode: editDialog.processCode, processName: editDialog.title })
}

// 详情查看
const detailDialog = reactive({ visible: false, title: '' })
const details = ref<any[]>([])

async function onShowDetails(p: { id: number; processCode?: string; processName?: string }) {
  try {
    detailDialog.title = p.processName || p.processCode || ''
    const res = await examineApi.getDefaultProcessDetails({ processCode: p.processCode, pageindex: 1, pageSize: 100 })
    details.value = (res.data?.data || []) as any[]
    detailDialog.visible = true
  } catch (e) {
    console.error('加载流程预设详情失败', e)
  }
}
</script>

<style scoped>
.process-basic {
  padding: 16px;
}
.toolbar { margin-bottom: 12px; }
.pretty-input { padding: 6px 12px; border:1px solid #d9d9d9; border-radius:6px; outline:none; transition:border-color .2s, box-shadow .2s; }
.pretty-input:focus { border-color:#409eff; box-shadow:0 0 0 2px rgba(64,158,255,.15); }
.pretty-select {
  padding:6px 10px;
  height:32px;
  line-height:1.2;
  border:1px solid #d9d9d9;
  border-radius:6px;
  background:#fff;
  color:#303133;
  outline:none;
  /* 强制启用原生下拉，避免自绘导致卡顿/发糊 */
  appearance:auto;
  -webkit-appearance:menulist;
  -moz-appearance:menulist;
  box-shadow:none;
  transition:none;
}
.pretty-select:focus {
  border-color:#409eff;
  box-shadow:none;
}
/* 避免对 option 施加额外样式，保持系统原生渲染 */
select.pretty-select option {
  display:initial;
  padding:initial;
  line-height:initial;
  background:initial;
  color:initial;
}
.table { width: 100%; border-collapse: collapse; background: #fff; }
.table th, .table td { border: 1px solid #eaeaea; padding: 8px; text-align: left; }
.table th { background: #f8f9fa; font-weight: 600; }
.tag { display:inline-block; padding: 2px 8px; background:#eef2ff; color:#1d4ed8; border:1px solid #c7d2fe; border-radius:999px; font-size:12px; }
.dialog-mask { position: fixed; inset: 0; background: rgba(0,0,0,0.45); display:flex; align-items:center; justify-content:center; }
.dialog { width: 440px; background:#fff; border-radius:6px; padding:16px; }
.form-row { display:flex; align-items:center; margin-bottom:12px; }
.form-row label { width:72px; color:#666; }
.form-row input, .form-row select { flex:1; padding:6px 12px; border:1px solid #d9d9d9; border-radius:4px; }
.type-select { width:100%; }
.dialog-actions { text-align:right; }
button { padding:6px 12px; margin-right:8px; cursor:pointer; border:1px solid #dcdfe6; background:#fff; border-radius:4px; }
button.primary { background:#007bff; border-color:#007bff; color:#fff; }
button.danger { background:#ff4d4f; color:#fff; border:none; }
button.plain { background:transparent; border:1px solid #ccc; }

.pagination { display:flex; justify-content:space-between; align-items:center; margin-top:12px; }
.pagination .page-size { display:flex; align-items:center; gap:8px; }
.page-size-group { display:inline-flex; background:#f0f2f5; border:1px solid #d9d9d9; border-radius:999px; padding:2px; }
.page-size-pill { border:none; background:transparent; padding:6px 12px; border-radius:999px; cursor:pointer; font-size:12px; color:#606266; }
.page-size-pill.active { background:#fff; color:#303133; border:1px solid #d9d9d9; }
.pager-controls { display:flex; align-items:center; gap:10px; }

/* 轻量自绘下拉，避免原生被全局样式污染且保证性能 */
.select-like {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 32px;
  padding: 0 10px;
  border: 1px solid #d9d9d9;
  border-radius: 6px;
  background: #fff;
  cursor: pointer;
  user-select: none;
}
.select-like .label { color:#303133; font-size:14px; }
.select-like .arrow { color:#909399; font-size:12px; }
.select-like:focus { outline:none; }
.select-like:focus-visible { outline: 2px solid #409eff; }

.select-menu {
  position: absolute;
  top: 36px;
  left: 0;
  min-width: 140px;
  max-height: 280px;
  overflow:auto;
  background:#fff;
  border:1px solid #d9d9d9;
  border-radius:6px;
  box-shadow: 0 6px 16px rgba(0,0,0,.1);
  z-index: 1000;
}
.select-menu li {
  padding:8px 12px;
  white-space:nowrap;
  cursor:pointer;
}
.select-menu li:hover { background:#f5f7fa; }
.select-menu li.active { color:#409eff; font-weight:600; }

/* 审批人标签样式 */
.approver-display {
  min-height: 20px;
}

.approver-list {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.approver-tag {
  display: inline-block;
  padding: 2px 6px;
  background: #f0f2f5;
  border: 1px solid #d9d9d9;
  border-radius: 3px;
  font-size: 12px;
  color: #666;
}

/* 删除行按钮样式 */
.delete-row-btn {
  padding: 4px 8px;
  background: #ff4d4f;
  color: white;
  border: none;
  border-radius: 3px;
  cursor: pointer;
  font-size: 12px;
  transition: background-color 0.2s;
}

.delete-row-btn:hover {
  background: #ff7875;
}

.no-delete {
  color: #ccc;
  font-size: 12px;
}
</style>

